#ifndef WOLFIE_H_INCLUDED
#define WOLFIE_H_INCLUDED

#include "tupla.h"
#include "terna.h"
#include "cuaterna.h"
#include "aed2.h"
#include "diccarray.h"
#include "titulo.h"
#include "promesa.h"
#include "auxiliares.h"
#include "diccTrie.h"
#include "conjheap.h"

#include <iostream>
#include <cassert>
#include <cstdlib>
#include <string>
#include <utility>
#include <cassert>

#define VENTA false
#define COMPRA true

using namespace std;
using namespace aed2;
typedef string Nombre;
typedef tupla<Promesa, Cliente> parpc;
typedef cuaterna<Lista<parpc>, conjheap, Conj<Titulo>::Iterador, Nat > infoTitulo;

class wolfie{

    public:
    /*Constructor por de Wolfie*/
    wolfie(Conj<Cliente>);

    /*Destructor de Wolfie*/
    ~wolfie();

    ///OBSERVADORES///
	/*agrega el titulo a wolfie*/
    void agregarTitulo(Titulo t);
	/*actualizar lla cotizacion de un titulo*/
    void actualizarCotizacion(Nombre nt, Nat cot);
	/*agregar promesa*/
    void agregarPromesa(Cliente c, Promesa p);
    /*Devuelve un iterador al conj de clientes*/
	Conj<Cliente>::const_Iterador clientes();

	/*Devuelve un iterador al conj de titulos*/
	Conj<Titulo>::const_Iterador titulos();

	/*Devuelve un iterador al conj de promesas de un cliente*/
	Conj<Promesa>::const_Iterador promesasDe(Cliente c);

	/*Devuelve la cantidad de acciones que posee ese cliente del titulo nt*/
	Nat accionesPorClientes(Cliente c, Nombre nt);

	/*Devuelve la cantidad de acciones que posee ese cliente del titulo nt*/
	bool enAlza(Nombre nt);


	///FUNCION PARA MOSTRAR POR PANTALLA//
    /*muestra la Cola por pantalla*/
    //~ ostream & operator<<(ostream &os) const;


    ///IGUALDAD OBSERVACIONAL///

    bool operator==(const wolfie& otro) const;

	private:
		
		diccarray< tupla< DiccionarioTrie<Nat>, Nat > >* accionesPorCliente_; //
		DiccionarioTrie<infoTitulo> promesasPorTitulos_; // 
		Conj<Titulo> titulos_; //conj titulos
		Conj<Cliente>  clientes_;
		terna<Cliente, Conj<Promesa> , bool> ultimoCliente_;
		
		//Conjunto auxiliar para guardar las acciones de los titulos de diccarray
		Conj<Nat*> auxConjNat; //para borrar los punteros al final


};


wolfie::wolfie(Conj<Cliente> cs){
	clientes_ = cs;

	Conj<Cliente>::Iterador itC = cs.CrearIt();

	ultimoCliente_.first=itC.Siguiente();
	ultimoCliente_.third=false;

	Lista<tupla<Cliente, tupla<DiccionarioTrie<Nat>, Nat> > > ls;

	while(itC.HaySiguiente()){
		tupla<Cliente, tupla<DiccionarioTrie<Nat>, Nat> > ter;
		ter.first = itC.Siguiente();
		(ter.second).second = 0;

		ls.AgregarAdelante(ter);
		itC.Avanzar();
	}

	accionesPorCliente_ = new diccarray< tupla< DiccionarioTrie<Nat>, Nat > >(ls,ls.Longitud());
}

wolfie::~wolfie(){
	delete accionesPorCliente_;

    //DESTRUCTOR DE promesasPorTitulos_
    Lista<string> auxLista = *promesasPorTitulos_.claves() ;

    Lista<string>::Iterador itLista = auxLista.CrearIt();

    while (itLista.HaySiguiente()) {

        delete promesasPorTitulos_.obtener(itLista.Siguiente());
        itLista.Avanzar();
	}

	//Destruye los nat de acciones
	Conj<Nat*>::Iterador itConj = auxConjNat.CrearIt();

    while(itConj.HaySiguiente()) {

        delete itConj.Siguiente();
        itConj.Avanzar();
	}

}

void wolfie::agregarTitulo(Titulo t){

	Conj<Titulo>::Iterador itT;
	int n = clientes_.Cardinal();
	itT = titulos_.Agregar(t);
	Lista<parpc > s;
	conjheap h(n);

	promesasPorTitulos_.definir(t.nombre(), *(new infoTitulo(s,h,itT, t.maxAcciones())) );

}

void wolfie::agregarPromesa(Cliente c, Promesa p){

	//validacion
	//~ if( !p.tipo() && accionesPorCliente_->definido(c) ){
		//~ 
			//~ cout << "Cliente:"<< c <<" con tipo de promesa" <<p.tipo() << " titulo: " << p.titulo() <<endl;
			//~ if ((accionesPorCliente_->obtener(c).first).estaDefinido(p.titulo())){
				//~ Nat cant= *(accionesPorCliente_->obtener(c).first.obtener(p.titulo())) ; 
				//~ cout <<"Cantidad de acciones:" << cant <<endl;
				//~ 
				//~ if (cant < p.cantidad()) {
					//~ cout<<"no tiene acciones de ese titulo1"<<endl;
					//~ assert( false);
				//~ }
			//~ }else{
				//~ cout<<"no tiene acciones de ese titulo2"<<endl;
					//~ assert(false);
			//~ }
	//~ }
	//validacion
	//~ cout <<"Paso la validacion"<<endl;
    //se agrega checkeo de ulitmo cliente
    if(ultimoCliente_.first == c && ultimoCliente_.third) {ultimoCliente_.second.Agregar(p);}

    infoTitulo* iTitulo;

    iTitulo = promesasPorTitulos_.obtener(p.titulo());


    if(p.tipo() == VENTA) {

        parpc pc(p,c);
        iTitulo->first.AgregarAtras(pc);


    } else {


        Nat n = accionesPorCliente_->obtener(c).second;

        //terna<Cliente,Nat,Promesa> = q(c,n,p);
        InfoCliente q;
        q.c = c;
        q.n = n;
        q.p = p;

        iTitulo->second.Agregar(q);
	}


}

Conj<Cliente>::const_Iterador wolfie::clientes(){
	Conj<Cliente>::const_Iterador auxIt;
    auxIt = clientes_.CrearIt();
    return auxIt;
}


Conj<Titulo>::const_Iterador wolfie::titulos(){
	Conj<Titulo>::const_Iterador auxIt;
    auxIt = titulos_.CrearIt();
    return auxIt;
}

Nat wolfie::accionesPorClientes(Cliente c, Nombre nt){

	Nat n=0;
	if (((accionesPorCliente_->obtener(c)).first).estaDefinido(nt)){
		n = *(((accionesPorCliente_->obtener(c)).first).obtener(nt));
	}
	return n;

}

bool wolfie::enAlza(Nombre nt){

	bool res;

	Titulo t((*promesasPorTitulos_.obtener(nt)).third.Siguiente());
	res = t.enAlza();

	return res;

}

bool tienePromesaV(Cliente c, Lista<parpc>& ls){

    Lista<parpc>::Iterador it = ls.CrearIt();
    bool res= false;

    while(it.HaySiguiente() && !res){
        res = ((it.Siguiente()).second == c);
        it.Avanzar();
    }

	return res;
}


bool tienePromesaC(Cliente c, conjheap& cs){

	Lista<InfoCliente>::Iterador it = cs.DameElementos();

	bool res= false;

	while(it.HaySiguiente() && !res){

		res = ((it.Siguiente()).c == c);
		it.Avanzar();
	}

	return res;

}

Promesa& damePromesaV(Cliente c, Lista<parpc>& ls){
		Lista<parpc>::Iterador it = ls.CrearIt();

        Promesa& res = it.Siguiente().first;

		while(it.HaySiguiente()){
			if((it.Siguiente()).second == c){
				res = it.Siguiente().first;
			}
			it.Avanzar();
		}
	return res; 
}

Promesa& damePromesaC(Cliente c, conjheap& cs){
		Lista<InfoCliente>::Iterador it = cs.DameElementos();

        Promesa& res = it.Siguiente().p;

		while(it.HaySiguiente()){
			if((it.Siguiente()).c == c){
				res = it.Siguiente().p;
			}
			it.Avanzar();
		}
	return res;

}

Conj<Promesa>::const_Iterador wolfie::promesasDe(Cliente c){

	Conj<Promesa>::const_Iterador res;

	 if( ultimoCliente_.third && (ultimoCliente_.first == c)){

		 res = (ultimoCliente_.second).CrearIt();

	 } else {

		 ultimoCliente_.first = c;
		 ultimoCliente_.third = true;
		 ultimoCliente_.second = Conj<Promesa>();

		 Conj<Titulo>::Iterador itT = titulos_.CrearIt();

		 while(itT.HaySiguiente()){

			 //typedef cuaterna<Lista<parpc>, conjheap , Conj<Titulo>::Iterador, Nat >
			 infoTitulo t;
			 Titulo tit = itT.Siguiente();

			 t = *promesasPorTitulos_.obtener(tit.nombre());

			 if(tienePromesaV(c, t.first)){

				 ultimoCliente_.second.Agregar(damePromesaV(c, t.first));
			}


			if(tienePromesaC(c, t.second)){
                ultimoCliente_.second.Agregar(damePromesaC(c, t.second));
			 }
			 itT.Avanzar();
		 }
		 res = (ultimoCliente_.second).CrearIt();
	 }

	return res;
}


void wolfie::actualizarCotizacion(Nombre nt, Nat cot){
	
	assert (promesasPorTitulos_.estaDefinido(nt));
	infoTitulo* tituloAct;
	tituloAct = promesasPorTitulos_.obtener(nt);
	bool ejecutoVenta = false;

	Titulo t = (tituloAct->third).Siguiente();
	(tituloAct->third).EliminarSiguiente();
	t.recotizar(cot);
	(tituloAct->third) = titulos_.Agregar(t);	

	//~ //Cumplir con promesas de venta
	Lista<parpc>::Iterador itVenta = (tituloAct->first).CrearIt();
	
	//~ cout<<"tamaño de la lista" << tituloAct->first.Longitud()<<endl;
	
	while(itVenta.HaySiguiente()){
		ejecutoVenta=true;

		if((itVenta.Siguiente().first).limite() >= cot){

			Nat* f = (accionesPorCliente_->obtener(itVenta.Siguiente().second)).first.obtener(nt) ; //obtengo el significado del trie
			*f = *f - (itVenta.Siguiente().first).cantidad();

			Nat tr = (accionesPorCliente_->obtener(itVenta.Siguiente().second).second);
			//el cambio se hace por referencia no hace falta volver a definirla
			tr = tr - *f; //Actualizo cantida total de acciones de todos los titulos 
			(accionesPorCliente_->obtener(itVenta.Siguiente().second).second) = tr;
			//~ //Actualizo acciones disponibles de ese titulo
			tituloAct->fourth = tituloAct->fourth  + (itVenta.Siguiente().first).cantidad();
			itVenta.EliminarSiguiente();
		}
		else{

			itVenta.Avanzar(); 
		}

	}

	if (ejecutoVenta){ //se agrego
		Conj<Cliente>::Iterador itCompra = clientes_.CrearIt();


		while(itCompra.HaySiguiente() && (tituloAct->second).Cardinal() > 0 ){
		//InfoCliente es typedef cuaterna<Lista<parpc>, conjheap , Conj<Titulo>::Iterador, Nat >

			InfoCliente aux = (tituloAct->second).Sacar();
			aux.n = (accionesPorCliente_->obtener(itCompra.Siguiente())).second;
			tituloAct->second.Agregar(aux);
			itCompra.Avanzar();

		}
	}

	Conj<terna<Cliente, Nat, Promesa> > q;				//q guarda promesas de compra que no se ejecutan
	 
	while(tituloAct->second.Cardinal() > 0){				
		
			InfoCliente prom = (tituloAct->second).Sacar();
			
			if ((prom.p).limite() < cot && (prom.p).cantidad() <= tituloAct->fourth){	//VER MAX_ACCIONES DE TITULO
			 
				if (!(accionesPorCliente_->obtener(prom.c).first).estaDefinido(prom.p.titulo())){

					Nat* auxN0 = new Nat(0);
					auxConjNat.Agregar(auxN0);
					(accionesPorCliente_->obtener(prom.c).first).definir(t.nombre(), *auxN0);
				}
				
				Nat n = *((accionesPorCliente_->obtener(prom.c).first).obtener(t.nombre()));
				n = n + (prom.p).cantidad();
				
				Nat* auxN = new Nat(n);
				auxConjNat.Agregar(auxN);

				(accionesPorCliente_->obtener(prom.c).first).definir(t.nombre(), *auxN);
				
				Nat n1 = accionesPorCliente_->obtener(prom.c).second;
				accionesPorCliente_->obtener(prom.c).second= n1+ n;
				 
				tituloAct->fourth = tituloAct->fourth - (prom.p).cantidad();//t.maxAcciones();
				 
			}else{
				//cout <<"no se pudo ejecutar una promesa"<<endl;
				terna<Cliente, Nat, Promesa> t_aux(prom.c, prom.n, prom.p);		
				q.Agregar(t_aux);
			}
			
		}	
		//agrego las promesas de compra que no se ejecutaron
		
		if(q.Cardinal() >0){
			Conj< terna<Cliente, Nat, Promesa> >::Iterador itp = q.CrearIt();
			InfoCliente ic;
			while(itp.HaySiguiente()){
				ic.c = itp.Siguiente().first;
				ic.n = itp.Siguiente().second;
				ic.p = itp.Siguiente().third;
				tituloAct->second.Agregar(ic);
				itp.Avanzar();
			}
		}
	//~ cout<<"Fin de actualizar cotizacion"<<endl;
}
bool wolfie::operator==(const wolfie& otro) const{
	
		
		

		//~ terna<Cliente, Conj<Promesa> , bool> ultimoCliente_;
		bool res=false;
		if(ultimoCliente_.third== otro.ultimoCliente_.third && !ultimoCliente_.third){
			res = res && true;
			res = res && (ultimoCliente_.first==otro.ultimoCliente_.first);
			res = res && (ultimoCliente_.second==otro.ultimoCliente_.second);
			
			}
		//~ Conj<Cliente>  clientes_;
		res = res && (clientes_ == otro.clientes_);
		
		//~ Conj<Titulo> titulos_;
		res = res && (titulos_ == otro.titulos_);
		
		//~ DiccionarioTrie<infoTitulo> promesasPorTitulos_; // 
		res = res && (promesasPorTitulos_ == otro.promesasPorTitulos_);
	
		//~ diccarray< tupla< DiccionarioTrie<Nat>, Nat > >* accionesPorCliente_; //
		res = res && (accionesPorCliente_ == otro.accionesPorCliente_);
	
		return res;
}

#endif	//WOLFIE_H_INCLUDED
